import os
import subprocess
from datetime import datetime

from ssh_run import SshRun


class InstallDb(SshRun):
    def __init__(self, **kwargs):
        super(InstallDb, self).__init__()
        self.u_pwd = kwargs['userpasswd']
        self.r_pwd = kwargs['rootpasswd']
        self.db_pwd = kwargs['dburpasswd']
        self.report_db_pwd = kwargs['report_db_passwd']
        self.report_db_host = kwargs['report_db_host']
        self.report_user = kwargs['report_user']
        self.report_db = kwargs['report_db']
        self.report_db_port = kwargs['report_db_port']
        self.func_user_prefix = kwargs['func_user_prefix']
        self.prefixPort = kwargs['prefixPort']
        self.db_root_path = kwargs['db_root_path']
        self.install_pkg_root_path = kwargs['pkg_root_path']
        self.db_version = kwargs['db_version']
        self.ftp_path = kwargs['ftp_path']
        self.date = datetime.strftime(datetime.now(), '%m%d')
        self.user_name = f"{self.func_user_prefix}{self.date}"
        self.port = f'{self.prefixPort}{self.date}'
        self.pkg_path = os.path.join(self.install_pkg_root_path,
                                     self.user_name, self.date, 'pkg')
        self.script_path = os.path.join(self.pkg_path, 'script')
        self.xml_path = os.path.join(self.pkg_path, f'{self.user_name}.xml')
        self.env_path = os.path.join('/home', self.user_name,
                                     'gaussdb.bashrc')
        self.log_path = os.path.join(os.getcwd(), 'log', 'run_log',
                                     'install_db',
                                     f'opengauss_{self.db_version}_'
                                     f'install.log')

    def create_trust(self, script_path, f_path):
        cmd = f'cd {script_path} && ./gs_sshexkey -f {f_path}'
        self.write_file(self.log_path, cmd)
        res = subprocess.getoutput(cmd)
        self.write_file(self.log_path, res)
        assert 'Successfully created SSH trust' in res

    def clean_env(self, *args):
        self.write_file(self.log_path, '开始清理环境')
        for i in args:
            self.write_file(self.log_path, i)
            ssh = self.ssh_connect(i, 'root', self.r_pwd)
            cmd = f"cat /etc/passwd | grep {self.func_user_prefix}| " \
                f"awk -F: '$3>=500' | cut -f 1 -d :"
            res = self.ssh_run(ssh, cmd, self.log_path)
            self.write_file(self.log_path, f'res: {res}')
            for old_u in res.splitlines():
                self.write_file(self.log_path, f'先删除用户{old_u}的定时任务')
                del_cmd = f'su - {old_u} -c "crontab -r"'
                run_res = self.ssh_run(ssh, del_cmd, self.log_path)
                self.write_file(self.log_path, f'run_res: {run_res}')

                self.write_file(self.log_path, f'再杀死用户{old_u}的进程')
                kill_cmd = f"\nps -u {old_u}|grep -v PID|" \
                    f"awk '{{print $1}}'|xargs -I {{}} kill -9 {{}} "
                q_cmd = f"\nps -o ruser=userForLongName -e -o pid,ppid," \
                    f"c,stime,tty,time,cmd|grep -v grep|grep {old_u}"
                for j in range(10):
                    self.write_file(self.log_path, f'第{j}次查询用户{old_u}的进程')
                    run_res = self.ssh_run(ssh, q_cmd, self.log_path)
                    self.write_file(self.log_path, f'run_res: {run_res}')
                    if len(run_res) > 0:
                        self.write_file(self.log_path, f'第{j}次杀死用户{old_u}的进程')
                        run_res = self.ssh_run(ssh, kill_cmd, self.log_path)
                        self.write_file(self.log_path, f'run_res: {run_res}')
                    else:
                        break

                self.write_file(self.log_path, f'最后清理用户{old_u}的目录')
                cmd = f"\nuserdel -r {old_u} && " \
                    f"rm -rf /home/{old_u} /root/gauss_om/{old_u} " \
                    f"{self.db_root_path}/{old_u} " \
                    f"{self.install_pkg_root_path}/{old_u} /dev/shm/*"
                run_res = self.ssh_run(ssh, cmd, self.log_path)
                self.write_file(self.log_path, f'run_res: {run_res}')

            self.write_file(self.log_path, '环境准备：清理内存、信号量')
            cmd = f"\nsync"
            cmd += f"\necho 3 > /proc/sys/vm/drop_caches"
            cmd += f"\nipcs -m|awk '{{if($3~/^[0-9]+$/) {{print $2}}}}'|" \
                f"xargs -I {{}} ipcrm -m {{}}"
            cmd += f"\nipcs -s|awk '{{if($3~/^[0-9]+$/) {{print $2}}}}'|" \
                f"xargs -I {{}} ipcrm -s {{}}"
            for c in cmd.splitlines():
                run_res = self.ssh_run(ssh, c, self.log_path)
                self.write_file(self.log_path, f'run_res: {run_res}')

            for j in range(1000):
                cmd = f"ls /home|grep {self.user_name}|awk '{{print $3}}'"
                res = self.ssh_run(ssh, cmd, self.log_path)
                self.write_file(self.log_path, res)
                if res == self.user_name or len(res) == 0:
                    break
                else:
                    self.user_name = f"{self.func_user_prefix}" \
                        f"{str(int(self.date) + j * 100).rjust(4, '0')}"
            self.write_file(self.log_path, self.user_name)

            for j in range(1000):
                cmd = f"lsof -i:{self.port}; " \
                    f"lsof -i:{int(self.port) + 1};" \
                    f"lsof -i:{int(self.port) + 4};" \
                    f"lsof -i:{int(self.port) + 5};" \
                    f"lsof -i:{int(self.port) + 20}"
                res = self.ssh_run(ssh, cmd, self.log_path)
                self.write_file(self.log_path, res)
                if len(res) != 0:
                    self.port = f"{self.prefixPort}" \
                        f"{str(int(self.date) + j * 100).rjust(4, '0')}"
                else:
                    break
            self.write_file(self.log_path, self.port)

            self.ssh_close(ssh)

    def prepare_user(self):
        self.write_file(self.log_path, '获取可用用户和端口，创建用户和组')
        cmd = f"groupadd {self.user_name}"
        cmd += f"\nuseradd -g {self.user_name} -d /home/{self.user_name} " \
            f"-m -s /bin/bash {self.user_name}"
        cmd += f"\necho {self.u_pwd} | passwd --stdin {self.user_name}"
        res = subprocess.getoutput(cmd)
        self.write_file(self.log_path, res)

        self.write_file(self.log_path,
                        '解决偶现groupadd新增用户后，丢失了.bashrc和.bash_profile的问题')
        cmd = f'ls -al /home/{self.user_name}|grep -w .bashrc'
        cmd1 = f'ls -al /home/{self.user_name}|grep -w .bash_profile'
        for c in (cmd, cmd1):
            self.write_file(self.log_path, c)
            res = subprocess.getoutput(c)
            self.write_file(self.log_path, res)
            if self.user_name not in res:
                cmd2 = f'cp /etc/skel/{c.split()[-1]} /home/{self.user_name}/'
                self.write_file(self.log_path, cmd2)
                res = subprocess.getoutput(cmd2)
                self.write_file(self.log_path, res)

    def get_pkg(self):
        self.write_file(self.log_path, '获取安装包')
        wget_cmd = f'wget {self.ftp_path}'

        cmd = f'mkdir -p {self.pkg_path};' \
            f'rm -rf {self.pkg_path}/*;' \
            f'cd {self.pkg_path} && {wget_cmd}'
        self.write_file(self.log_path, cmd)
        res = subprocess.getoutput(cmd)
        assert '100%' in res

        cmd = f"ls {self.pkg_path}|grep {wget_cmd.split('/')[-1]}"
        self.write_file(self.log_path, cmd)
        res = subprocess.getoutput(cmd).strip()
        assert res == wget_cmd.split('/')[-1]

        cmd = f'cd {self.pkg_path} && tar -zxf "{wget_cmd.split("/")[-1]}"'
        self.write_file(self.log_path, cmd)
        res = subprocess.getoutput(cmd)
        self.write_file(self.log_path, res)

        f_list = os.listdir(self.pkg_path)
        for f in f_list:
            if f.endswith('tar.gz') and f != wget_cmd.split('/')[-1]:
                cmd = f'cd {self.pkg_path} && tar -zxf "{f}"'
                self.write_file(self.log_path, cmd)
                res = subprocess.getoutput(cmd)
                self.write_file(self.log_path, res)

    def prepare_xml(self, **kargs):
        self.write_file(self.log_path, '开始准备xml')
        hostnames = ','.join(kargs.values())
        ips = ','.join(kargs.keys())
        base_path = os.path.join(self.db_root_path, self.user_name, 'cluster')
        xml = f'''<?xml version="1.0" encoding="UTF-8"?>
<ROOT>
    <CLUSTER>
        <PARAM name="clusterName" value="{self.user_name}"/>
        <PARAM name="nodeNames" value="{hostnames}"/>
        <PARAM name="backIp1s" value="{ips}"/>
        <PARAM name="gaussdbAppPath" value="{os.path.join(base_path, 
                                                          'app')}"/>
        <PARAM name="gaussdbLogPath" value="{os.path.join(base_path,
                                                          'gaussdb_log')}"/>
        <PARAM name="tmpMppdbPath" value="{os.path.join(base_path, 'tmp')}"/>
        <PARAM name="gaussdbToolPath" value="{os.path.join(base_path,
                                                           'tool')}"/>
        <PARAM name="corePath" value="{os.path.join(base_path, 'corefile')}"/>
        <PARAM name="clusterType" value="single-inst"/>
    </CLUSTER>
    <DEVICELIST>
        <DEVICE sn="{list(kargs.values())[0]}">
            <PARAM name="name" value="{list(kargs.values())[0]}"/>
            <PARAM name="azName" value="AZ1"/>
            <PARAM name="azPriority" value="1"/>
            <PARAM name="backIp1" value="{list(kargs.keys())[0]}"/>
            <PARAM name="sshIp1" value="{list(kargs.keys())[0]}"/>
            <PARAM name="dataNum" value="1"/>
            <PARAM name="dataPortBase" value="{self.port}"/>
            <PARAM name="dataPortStandby" value="{self.port}"/>
            <PARAM name="dataNode1" value="{os.path.join(base_path, 'dn1')},{
        list(kargs.values())[1]},{os.path.join(base_path, 'dn1')},{
        list(kargs.values())[2]},{os.path.join(base_path, 'dn1')}"/>
        </DEVICE>
        <DEVICE sn="{list(kargs.values())[1]}">
            <PARAM name="name" value="{list(kargs.values())[1]}"/>
            <PARAM name="azName" value="AZ1"/>
            <PARAM name="azPriority" value="1"/>
            <PARAM name="backIp1" value="{list(kargs.keys())[1]}"/>
            <PARAM name="sshIp1" value="{list(kargs.keys())[1]}"/>
        </DEVICE>
        <DEVICE sn="{list(kargs.values())[2]}">
            <PARAM name="name" value="{list(kargs.values())[2]}"/>
            <PARAM name="azName" value="AZ1"/>
            <PARAM name="azPriority" value="1"/>
            <PARAM name="backIp1" value="{list(kargs.keys())[2]}"/>
            <PARAM name="sshIp1" value="{list(kargs.keys())[2]}"/>
        </DEVICE>
    </DEVICELIST>
</ROOT>'''
        with open(self.xml_path, 'w', encoding='utf-8', errors='ignore') as f:
            f.write(xml)
        self.write_file(self.log_path, '准备xml完成')

    def update_xml_for_cm(self, **kargs):
        self.write_file(self.log_path, '修改xml为cm方式')
        hostnames = ','.join(kargs.values())
        ips = ','.join(kargs.keys())
        cm_path = os.path.join(self.db_root_path, self.user_name,
                               'cluster', 'cm')
        cm_port = str(int(self.port) + 20)
        cmd = f'sed -i "/dataNum/i\\            ' \
            f'<PARAM name=\\"cmsNum\\" value=\\"1\\"/>" {self.xml_path}'
        cmd += f'\nsed -i "/dataNum/i\\            ' \
            f'<PARAM name=\\"cmServerPortBase\\" value=\\"{cm_port}\\"/>" ' \
            f'{self.xml_path}'
        cmd += f'\nsed -i "/dataNum/i\\            ' \
            f'<PARAM name=\\"cmServerlevel\\" value=\\"1\\"/>" ' \
            f'{self.xml_path}'
        cmd += f'\nsed -i "/dataNum/i\\            ' \
            f'<PARAM name=\\"cmServerListenIp1\\" value=\\"{ips}\\"/>" ' \
            f'{self.xml_path}'
        cmd += f'\nsed -i "/dataNum/i\\            ' \
            f'<PARAM name=\\"cmServerRelation\\" ' \
            f'value=\\"{hostnames}\\"/>" {self.xml_path}'
        cmd += f'\nsed -i "/azPriority/a\\            ' \
            f'<PARAM name=\\"cmDir\\" value=\\"{cm_path}\\" />" ' \
            f'{self.xml_path}'
        cmd += f'\nsed -i "/dataPortStandby/d" {self.xml_path}'
        self.write_file(self.log_path, cmd)
        res = subprocess.getoutput(cmd)
        self.write_file(self.log_path, res)
        assert len(res) == 0

    def preinstall(self):
        self.write_file(self.log_path, '开始预安装')
        install_path = os.path.join(self.db_root_path, self.user_name)
        cmd = f'''mkdir -p {install_path}
            chown -R {self.user_name}:{self.user_name} {install_path}
            chmod -R 755 {install_path}
            chmod -R 755 {os.path.join(self.install_pkg_root_path,
                                       self.user_name)}
            rm -rf ~/.ssh/*
            source ~/.bashrc
            '''
        for c in cmd.splitlines():
            self.write_file(self.log_path, c)
            res = subprocess.getoutput(c)
            self.write_file(self.log_path, res)

        cmd = f'''cd {self.script_path}
            expect <<EOF
            set timeout 1800
            spawn ./gs_preinstall -U {self.user_name} -G {self.user_name} \
            -X {self.xml_path} --sep-env-file={self.env_path}
            expect {{
                "*(yes/no)?" {{ send "yes\\n"; exp_continue }}
                "*assword:" {{ send "{self.r_pwd}\\n" }}
            }}
            expect {{
                "*(yes/no)?" {{ send "yes\\n"; exp_continue }}
                "*assword:" {{ send "{self.u_pwd}\\n"; exp_continue }}
                "*assword:" {{ send "{self.u_pwd}\\n" }}
                eof
            }}''' + '\nEOF\n'
        self.write_file(self.log_path, cmd)
        res = subprocess.getoutput(cmd)
        self.write_file(self.log_path, res)
        assert 'Preinstallation succeeded' in res

    def install(self, host):
        self.write_file(self.log_path, '开始安装')
        cmd = f'chmod 777 {self.xml_path}'
        self.write_file(self.log_path, cmd)
        res = subprocess.getoutput(cmd)
        self.write_file(self.log_path, res)

        ssh = self.ssh_connect(host, self.user_name, self.u_pwd)
        cmd = f'''source {self.env_path};
            expect <<EOF
            set timeout 1800
            spawn gs_install -X {self.xml_path} \
            --dn-guc=modify_initial_password=false
            expect {{
                "yes/no" {{ send "yes\\n"; exp_continue }}
                "*database:" {{ send "{self.u_pwd}\\n"; exp_continue }}
                "*database:" {{ send "{self.u_pwd}\\n" }}
                eof
            }}''' + '\nEOF\n'
        res = self.ssh_run(ssh, cmd, self.log_path)
        self.write_file(self.log_path, res)
        self.ssh_close(ssh)
        assert 'Successfully started cluster' in res
        assert 'Successfully installed application' in res

    def set_param(self, *args):
        self.write_file(self.log_path, '安装完成，配置参数')
        ssh = self.ssh_connect(args[0], self.user_name, self.u_pwd)
        cmd = f"source {self.env_path};"
        cmd += f"gs_guc set -N all -I all -c 'backtrace_min_messages=error'"
        cmd += f" && gs_guc set -N all -I all -h " \
            f"'host all tpccuser {args[0]}/32 sha256'"
        cmd += f" && gs_guc set -N all -I all -h " \
            f"'host all tpccuser {args[1]}/32 sha256'"
        cmd += f" && gs_guc set -N all -I all -h " \
            f"'host all tpccuser {args[2]}/32 sha256'"
        cmd += f" && gs_guc set -N all -I all -c 'most_available_sync=on'"
        cmd += f" && gs_om -t restart"
        cmd += f" && gsql -d postgres -p {self.port} -c '" \
            f"create database tpccdb; " \
            f"create user tpccuser identified by \"{self.u_pwd}\"; " \
            f"grant all privileges to tpccuser;'"
        res = self.ssh_run(ssh, cmd, self.log_path)
        self.write_file(self.log_path, res)
        self.ssh_close(ssh)
        assert res.count('Failed instances: 0') == 5
        assert 'Successfully stopped cluster' in res
        assert 'Successfully started' in res
        assert 'CREATE DATABASE' in res
        assert 'CREATE ROLE' in res
        assert 'ALTER ROLE' in res

    def get_db_vesion(self, host):
        self.write_file(self.log_path, '获取数据库版本信息')
        cmd = f'''su - {self.user_name} -c "source {self.env_path}; \
        gsql -d postgres -p {self.port} -c \\"select version();\\""|
        awk 'NR==3' '''
        self.write_file(self.log_path, cmd)
        cur_db_version = subprocess.getoutput(cmd)
        self.write_file(self.log_path, cur_db_version)
        assert f'openGauss {self.db_version} build' in cur_db_version
        install_time = datetime.strftime(datetime.now(), '%Y-%m-%d %H:%M:%S')
        if host == self.report_db_host:
            ssh = self.ssh_connect(self.report_db_host, self.report_user,
                                   self.report_db_pwd)
            cmd = f"source /home/{self.report_user}/gaussdb.bashrc"
            cmd += f"\ngsql -d {self.report_db} " \
                f"-p {self.report_db_port} -c \"" \
                f"delete from \\\"ReportApp_dbverison\\\" " \
                f"where feature='upgrade'; " \
                f"insert into \\\"ReportApp_dbverison\\\"" \
                f"(feature, db_version, install_time) values " \
                f"('master_function', E'{cur_db_version}'," \
                f" '{install_time}');\""
            res = self.ssh_run(ssh, cmd, self.log_path)
            self.write_file(self.log_path, res)
            self.ssh_close(ssh)

